﻿using System.Collections.Generic;
using System.Linq;
using ALDIOrganizer;

namespace Generator
{
    /// <remarks>
    /// This part of the Engine class consists of the big ImportData method, whose task is to
    /// translate a Template, and a CurrentFile of _staff, into the slim classes/structs used
    /// by our engine. 
    /// For example, we are compiling a "list of possible Candidates" for each Shift.
    /// We are also seeking for every group of Shifts that overlap (because if two or more Shifts
    /// overlap in Time, then obviously they must be worked by different emps_from_database).
    /// </remarks>    
    public partial class Engine
    {
        /// <summary>
        /// int array containing numbers 0 to 6
        /// </summary>
        /// <remarks>
        /// Engine.ImportData method takes an int[] parameter which tells the engine which bits of the week
        /// it is expected to take into consideration.
        /// This is done because as we are looking for impossibilities, we will be trying to build
        /// (and solve) a solution for every Day of the week SEPARATE, so that the engine doesn't choke
        /// On complexity of the task.
        /// Basically then, wholeWeek is just for convenience.
        /// </remarks>
        public static int[] WholeWeek()
        {
            var alldays = new[] { 0, 1, 2, 3, 4, 5, 6 };
            return alldays;
        }

        /* returns -1 if it finds an obvious impossibility (a Shift with nobody to work it)
         * 
         * Sometimes, however, "impossibilities" may not be that obvious.
         * For example: imagine that there'pShift two emps_from_database available On Monday - Bob and Jack.
         * But our Template says we need 3 Shifts, Open-Close.
         * 
         * We're short On people, but it'pShift not obvious to detect.
         * Shift A will have two Candidates: Bob and Jack,
         * Shift B will have two Candidates (Bob and Jack),
         * and Shift C will also have two Candidates.
         * 
         * None of them will "realize" that there'pShift no way they can successfully split the _staff between themselves
         * so that every Shift gets an _employee.
         * 
         * This could be problematic, because they will keep On trying, and in the worst scenario
         * the engine could Finish up analyzing milions of possible combination, just to fail at 99% each Time
         * (repeatedly finding that one Shift noone can take).
         * [...]         */
        public int ImportData(Rota rota, Template template, Weekday[] days)
        {
            this.solution = new Solution(parameters); // parameters 
            this._template = template;
            this._rota = rota;

            /* We're using those temporary variables because it'pShift easier to build our data up
             * without calculating in advance how big it will be.
             * 
             * At the Finish of this method, all the data collected in these "temps"
             * will be saved into respective Solution properties of fixed size. */
            var tempShifts = new List<Shift>();

            dicCandidatesStaff = new Dictionary<Candidate, EmployeeInRota>();




            var howManyShifts = _template.ShiftsCount(days);
            var howManyEmployees = _rota.Employees.getWorkers.Count();

            
            // 

            solution.Candidates = new Candidate[howManyEmployees];
            solution.Shifts = new Shift[howManyShifts];
            // 

            solution.shift_emp = new bool[howManyShifts, howManyEmployees];
            solution.openers = new bool[7, howManyEmployees];
            solution.closers = new bool[7, howManyEmployees];
            solution.keyflowchain = new bool[7];
            solution.keyflowValue = 0;


            #region building Candidates CurrentFile (solution.Candidates)
            
            int index = 0;
            // 

            int ID = 0;
            
            foreach (var e in _rota.Employees.getWorkers)
            {                
                var guy = new Candidate
                              {
                                  ID = ID,
                                  IsOnVisa = e.Employee.OnVisa,
                                  IsStoreRunner = e.IsStoreRunner,
                                  ContractHrs = e.ContractHours,
                                  Days = new bool[7],
                                  Revisits = new bool[7],
                                  FullDays = new bool[7]
                              };
                if (parameters.LastWeekClosers != null &&
                    parameters.LastWeekClosers.Contains(e.Employee.ID))
                {
                    guy.ClosesOnPreviousSunday = true;
                }
                solution.Candidates[index] = guy;
                index++;
                dicCandidatesStaff.Add(guy, e);

                ID++;
            }
            #endregion


            ID = 0;

            foreach (var day in days)
            {
                // ^ remember, this iteration of the loop only covers one particular Day!
                var thisDay = new Dictionary<ALDIOrganizer.Shift, Shift>();
                foreach (var s in template.GetAllShiftsForColumn(day.ToInt1()))
                {
                    // COME BACK HERE!!!
                    // pShift.GetParent = Rota;

                    solution.OrphanedShiftsLeft += 1;

                    /* we are "translating" Shifts (from Template) to engShifts - specific, simplified class
                     * which we will use for the algorithm */
                    #region basic data
                    var es = new Shift
                                 {
                                     MyOverlaps = new int[0],
                                     Day = day.ToInt0(),
                                     FromTo = s.ToShiftStub(),
                                     ID = ID,
                                     length = (byte) (s.Length().GetTotal*4),
                                     role = s.JobPosition,
                                     IsTaken = false,
                                     Chosen = -1
                                 };
                    ID++;
                    #endregion

                    // find Candidates
                    #region attributing Candidates
                    // 
                    var candidatesForThisShift = new List<int>();

                    foreach (var c in dicCandidatesStaff.Keys)
                    {
                        // only those Candidates who can work a certain Shift - candidate.g. aren't On _holidays
                        if (!dicCandidatesStaff[c].CanWork(s) ||
                            !dicCandidatesStaff[c].HisPosition.Matches(es.role))
                        {
                            continue;
                        }
                        solution[es.ID, c.ID] = true;
                        es.CandidatesLeft += 1;
                        candidatesForThisShift.Add(c.ID);
                    }

                    es.Candidates = candidatesForThisShift.ToArray();
                    
                    if (es.CandidatesLeft == 0) 
                    {
                        return -1;
                    }


                    #endregion

                    thisDay.Add(s, es);
                }

                /* so, we already have our nice Shift/engShift dictionary
                 * 
                 * what we haven't done yet is to supply a list of "overlapping" Shifts for each of our engShifts.
                 * 
                 * we are re-translating thisDay again, into an array.
                 * 
                 * this is done because we need to check every Shift against every Shift (for overlapping),
                 * but we need to access them by numeric index, not by their Dictionary key.
                 * 
                 * why? well, because I don't want to duplicate the data. _howManyWeHave want to compare Shift number 3 with Shift number 5
                 * only once.
                 * otherwise, Shift number 3 would learn that it overlaps with Shift #5 twice.
                 * id1, When we'd compare 3 vs 5, and then again When we'd compare 5 vs 3. data would be doubled.
                 * 
                 * so I just translate the dictionary into an array, and note what is done: ... */

                var thisDay2 = thisDay.ToArray();

                if (thisDay2.Length > 1)
                    for (int i = 0; i < thisDay2.Length; i++)

                        // right here! "for j = _howManyWeHave+1". not "for j = 0".
                        // that'pShift the point. we will not compare 3 / 5, and then 5 / 3.
                        for (int j = i + 1; j < thisDay2.Length; j++)
                            if (i != j)
                            {
                                var temp = new List<ALDIOrganizer.Shift>
                                               {
                                                   thisDay2[i].Key,
                                                   thisDay2[j].Key
                                               };
                                if (ALDIOrganizer.Shift.DoOverlap(temp.ToArray()))
                                {
                                    /* WHY SO FUNNY AGAIN?
                                     * it'pShift the cost of using structs.
                                     * 
                                     * we cannot modify thisDay2[pException].Value.myOverlaps directly.
                                     * so we have to make a "snapshot" of the keyvaluepair, modify it,
                                     * then replace it as a element in our array. */
                                    var copy = thisDay2[i];
                                    var copyEngshift = copy.Value;
                                    copyEngshift.MyOverlaps = ExtendIntArray(copyEngshift.MyOverlaps, thisDay2[j].Value.ID);
                                    copy = new KeyValuePair<ALDIOrganizer.Shift, Shift>(thisDay2[i].Key, copyEngshift);
                                    thisDay2[i] = copy;

                                    copy = thisDay2[j];
                                    copyEngshift = copy.Value;
                                    copyEngshift.MyOverlaps = ExtendIntArray(copyEngshift.MyOverlaps, thisDay2[i].Value.ID);
                                    copy = new KeyValuePair<ALDIOrganizer.Shift, Shift>(thisDay2[j].Key, copyEngshift);
                                    thisDay2[j] = copy;

                                    //thisDay2[_howManyWeHave].Value.myOverlaps=extendIntArray(thisDay2[_howManyWeHave].Value.myOverlaps,thisDay2[j].Value.id);
                                    //thisDay2[j].Value.myOverlaps.Add(thisDay2[_howManyWeHave].Value.id);
                                }
                            }

                tempShifts.AddRange(thisDay2.Select(kv => kv.Value));
            } // END LOOP:::::::  for (int Day = 0; Day < 7; Day++) 

            
            solution.Shifts = tempShifts.ToArray();
                        
            return 0;
        }


    }
}
